package com.carter.springframework.aop.config;

import com.carter.springframework.aop.aspect.Aspect;
import com.carter.springframework.aop.aspect.AspectJExpressionPointcutAdvisor;
import com.carter.springframework.aop.aspect.Before;
import com.carter.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor;
import com.carter.springframework.beans.exception.BeanException;
import com.carter.springframework.beans.factory.BeanFactory;
import com.carter.springframework.beans.factory.BeanFactoryAware;
import com.carter.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.carter.springframework.beans.factory.config.BeanDefinition;
import com.carter.springframework.beans.factory.config.BeanDefinitionRegistryPostProcessor;
import com.carter.springframework.beans.factory.config.PropertyValue;
import com.carter.springframework.beans.factory.config.PropertyValues;
import com.carter.springframework.beans.factory.support.BeanDefinitionRegistry;

import java.lang.reflect.Method;

public class AopBeanRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {
    private ConfigurableListableBeanFactory beanFactory;
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeanException {
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);
            Class<?> clazz = beanDefinition.getBeanClass();
            Aspect aspect = clazz.getAnnotation(Aspect.class);
            if (aspect!=null){
                Method[] methods = clazz.getDeclaredMethods();
                for (Method method : methods) {
                    Before before = method.getAnnotation(Before.class);
                    if (before!=null){
                        BeanDefinition methodBeforeAdviceInterceptor= new BeanDefinition(MethodBeforeAdviceInterceptor.class);
                        PropertyValues pvs = new PropertyValues();
                        pvs.addPropertyValue(new PropertyValue("advice",beanFactory.getBean(beanDefinitionName)));
                        methodBeforeAdviceInterceptor.setPropertyValues(pvs);
                        registry.registerBeanDefinition(beanDefinitionName+"MethodBeforeAdviceInterceptor", methodBeforeAdviceInterceptor);

                        BeanDefinition pointcutAdvisor = new BeanDefinition(AspectJExpressionPointcutAdvisor.class);
                        pvs = new PropertyValues();
                        pvs.addPropertyValue(new PropertyValue("advice",beanFactory.getBean(beanDefinitionName + "MethodBeforeAdviceInterceptor")));
                        pvs.addPropertyValue(new PropertyValue("expression", before.value()));
                        pointcutAdvisor.setPropertyValues(pvs);
                        registry.registerBeanDefinition(beanDefinitionName + "PointcutAdvisor", pointcutAdvisor);
                    }
                }
            }
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) {
        return;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeanException {
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }
}
